<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="../src/css/default.css">
    <link rel="stylesheet" href="./style.css">
    <title>闭包相关的问题</title>
</head>

<body>

    <ul class="top">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
    </ul>

    <br>

    <ul class="bottom">
        <li>5</li>
        <li>4</li>
        <li>3</li>
        <li>2</li>
        <li>1</li>
    </ul>

    <script src="./index.js"></script>
    <!--
        在讲闭包之前，先讲一个关于变量提升的经典案例： 循环绑定事件
            要求：请使用 var 声明的方式 完成页面中每个 li标签的绑定事件；
                即 点击每个 li 标签 打印其中的内容；
                var lis = document.querySelectorAll('li')

                for (var i = 0; i < [...lis].length; i++) { lis[i].onclick=()=> {
                        try {
                            console.log(lis[i].innerText)
                        } catch (error) {
                            console.log(error) // 打印报错信息
                        }
                            console.log(i) // 不管点击哪个 i 永远都是 5
                        }
                    }

                    console.dir(li) 可以将li对象强制展开

            这是为什么？ 
                是因为var在 for 代码块内声明的 i 被提升到全局作用域了，
                并且 i 的值已经被累加到了 5 所以不管点击哪个 都会是 5；
                自然就访问不到 lis 列表中元素了...

            其实这个问题的解决方法很简单...直接把var i 改成 let i 就可以；
            
            但是在ES6之前，没有let const, 该怎么解决？ 

            其实就可以使用闭包来解决；

        闭包：
            先问几个问题：

            1.作用域一直都存在吗？
                并不是，只有代码运行的时候 才会存在；
                js中有一个垃圾回收机制，用来将不再使用的作用域销毁，节省内存空间；
                
            2.为什么垃圾回收机制知道哪些作用域没有用了？
                因为 某个作用域执行完毕之后 就没有办法再访问里面的内容了！
                这里主要针对函数而言；

                函数作用域 运行时生效 运行完之后 其作用域就立马销毁掉了；

                func foo(a){
                    var b = 1
                    log(a + b)
                }

                fun(2) // 3 运行完毕之后 整个函数作用域就消失了
                我们无法在外部去访问到函数内部的变量 b;

            3.可能会有新的疑问：如果用 return 把 b 返回出来呢？
                func foo(a){
                    var b = 1
                    log(a + 1)
                    return b
                }
                let b = foo(2) // 3
                log(b) // 1

                乍一看 确实是访问到函数内部的 b 了，但真的如此吗？
                其实不然，return返回的 是一个确定的 值！ 并不是变量；
                所以还是无法直接访问到 内部的变量 b 无法在外部对它进行一些操作；

            4.如何在外部访问到函数内部的变量呢？
                就是让函数运行之后 还能保留其作用域； 
                只要函数里的变量 还存在被访问的机会 函数作用域就不会消失！
                
                那么，只要在函数内部 返回一个新的函数，那么函数执行之后
                只要这个新函数没有执行，那函数的作用域就不会消失；

                func foo(){
                    let a = 3
                    func foo1(){
                        log(a)
                    }

                    return foo1
                }

                let foo2 = foo() // foo 已经运行完毕了 理论上 作用域应该销毁了，
                foo2() // 3 但这里执行的时候 访问的变量还是foo的 a 变量
                说明 其作用域没有消失！

        闭包定义：
            一个函数的运行结果 是另一个函数 这个函数可以保留原函数，
            或者说还原 原函数运行时候的作用域；

            或者说 就是在一个函数里 嵌套另一个或多个函数，
            当该函数执行之后，只要内部嵌套的函数还有机会被执行，
            就能保留该函数执行时候的作用域，也叫做 闭包的缓存；

            举个例子：

            func 案发现场(){
                let 罪犯 = 杀了一个人

                let 你 = 路过 看到了尸体、罪犯 和 其他路人

                return 你
            }

            警察去调查案发现场：

            警察调查 = 案发现场() 找到了 你

            你虽然看到了尸体，但是能确定 罪犯是谁吗？ 不能确定！
                
            但是 如果：
            func 案发现场(){
                let 罪犯 = 杀了一个人

                let 你的手机 = func (){
                    拍到了凶手 杀人的过程
                }

                return 你的手机
            }

            警察调查 = 案发现场() 发现了你的手机
            警察调查() // 查看手机视频 就看到了当时的案发现场

        使用闭包就可以解决上面提到的循环绑定翻车的问题；

        闭包的缺陷：
            导致内存泄露 内存溢出 造成不必要的资源占用！尤其对于后端服务来说；

        如何释放内存？ 也就是清除闭包；闭包使用完之后 在合适的时候 用null清空闭包的缓存；

        btn.onclick = (()=>{
            var s = 1
            return ()=>{
                return s++
            }
        })()

        btn.onclick = null  让访问闭包的函数 无法重见天日 作用域就释放了！

    -->
</body>

</html>